﻿namespace LizardChess
open System
open System.Windows.Forms
open System.Drawing
open System.Linq
open System.IO
open System.Reflection
open DGVPrinterHelper
open Dialogs
module main =
    let SQUARE_SIZE = 42
    let SQUARE_BRIGHTNESS = 48
    let BOARD_SQUARE_COLOUR_WHITE = Color.FromArgb(202,255,112)
    let BOARD_SQUARE_COLOUR_BLACK = Color.FromArgb(50, 205, 50)
    let BOARD_SQUARE_COLOUR_WHITE_BRIGHT = Color.FromArgb(Math.Min(169 + SQUARE_BRIGHTNESS, 255), Math.Min(147 + SQUARE_BRIGHTNESS, 255), Math.Min(83 + SQUARE_BRIGHTNESS, 255))
    let BOARD_SQUARE_COLOUR_BLACK_BRIGHT = Color.FromArgb(Math.Min(149 + SQUARE_BRIGHTNESS, 255), Math.Min(117 + SQUARE_BRIGHTNESS, 255), Math.Min(53 + SQUARE_BRIGHTNESS, 255))
    let mutable m_squareTo = -1
    let mutable CurrPosn = Posn.st()
    let mutable CurrVarn = Varn.emp

    let vm(r,c) = c/2, r*2 + c%2
    let rc(v,m) = m/2, v*2 + (if m%2=1 then 1 else 0)
    do 
        Application.EnableVisualStyles()
        Application.SetCompatibleTextRenderingDefault(false)
    let frm=
        let this=new frmMain()
        // utilities
        let sv_sm() = vm(this.varnDGV.CurrentCell.RowIndex,this.varnDGV.CurrentCell.ColumnIndex)
        // ListView
        // updateLV
        let updateLV() =
            let nmvs = Varn.findnmvs CurrPosn CurrVarn.brchs
            this.mvsListView.Items.Clear()
            let addit (m:move) = 
                let itm = new ListViewItem()
                itm.Text <- m.PGN
                itm.Tag <- m.UCI
                this.mvsListView.Items.Add itm |>ignore
            nmvs |> List.iter addit
        // BOARD
        // renderBoardColours
        let renderBoardColours(sqFrom,movesPossible) =
            for i in [0..2 ..6] do
                for j  in [0..2..6] do
                    this.m_picSquares.[i, j].BackColor <- BOARD_SQUARE_COLOUR_BLACK
                    this.m_picSquares.[i + 1, j + 1].BackColor <- BOARD_SQUARE_COLOUR_BLACK
                    this.m_picSquares.[i, j + 1].BackColor <- BOARD_SQUARE_COLOUR_WHITE
                    this.m_picSquares.[i + 1, j].BackColor <- BOARD_SQUARE_COLOUR_WHITE
            if (sqFrom <> -1) then
                for move in movesPossible do
                    let picTo = this.m_picSquares.[Ref.fi.[move.To], Ref.ri.[move.To]]
                    picTo.BackColor <- if picTo.BackColor = BOARD_SQUARE_COLOUR_WHITE then BOARD_SQUARE_COLOUR_WHITE_BRIGHT else BOARD_SQUARE_COLOUR_BLACK_BRIGHT
        // renderBoard
        let renderBoard(sqFrom,movesPossible) =
            renderBoardColours(sqFrom,movesPossible)
            for i = 0 to 63 do
                let sq = CurrPosn.Sqs.[i]
                if (sq = -1 || sq = sqFrom) then
                    this.m_picSquares.[Ref.fi.[i], Ref.ri.[i]].Image <- null
                else
                    this.m_picSquares.[Ref.fi.[i], Ref.ri.[i]].Image <- this.imgPieces.Images.[Posn.imageIndex(sq, CurrPosn)]
                this.m_picSquares.[Ref.fi.[i], Ref.ri.[i]].BorderStyle <- BorderStyle.None
            if (CurrPosn.Mhst.Length > 0) then
                this.m_picSquares.[Ref.fi.[CurrPosn.Mhst.[0].From], Ref.ri.[CurrPosn.Mhst.[0].From]].BorderStyle <- BorderStyle.Fixed3D
                this.m_picSquares.[Ref.fi.[CurrPosn.Mhst.[0].To], Ref.ri.[CurrPosn.Mhst.[0].To]].BorderStyle <- BorderStyle.Fixed3D
            this.Refresh()
        // varnDGV
        // highlightCell
        let highlightCell() =
            let oselvar = Varn.findsv CurrPosn CurrVarn.brchs
            if oselvar.IsSome then
                let selvar = oselvar.Value
                let selmv = CurrPosn.Mhst.Length-1
                let rw,cl = rc(selvar,selmv)
                this.varnDGV.CurrentCell <- this.varnDGV.[cl, rw]
                this.varnDGV.Refresh()
        // diff
        let diff(dv,dm) =
            if this.varnDGV.RowCount > 0 then
                let oselvar,oselmv = sv_sm()
                let selvar = max 0 (min (oselvar + dv) (CurrVarn.brchs.Length-1))
                let selmv = max 0 (min (oselmv+dm) (CurrVarn.brchs.[selvar].Mhst.Length-1))
                let rw,cl = rc(selvar,selmv)
                let mvl = Varn.mvl(CurrVarn, selvar, selmv)
                CurrPosn <- Posn.DoMoves(mvl, Posn.st())
                updateLV()
                renderBoard(-1,[])
                this.varnDGV.CurrentCell <- this.varnDGV.[cl, rw]
                this.varnDGV.Refresh()
        this.homeButton.Click.Add(fun _ -> diff(0,-999))
        this.nextButton.Click.Add(fun _ -> diff(0,1))
        this.prevButton.Click.Add(fun _ -> diff(0,-1))
        this.endButton.Click.Add(fun _ -> diff(0,999))
        this.prevLineButton.Click.Add(fun _ -> diff(-1,0))
        this.nextLineButton.Click.Add(fun _ -> diff(1,0))
        this.varnDGV.CellMouseClick.Add(fun(e) -> diff(0,0))
        this.varnDGV.CellDoubleClick.Add(fun(e) -> diff(0,0))
        // keyDown
        let keyDown(e:KeyEventArgs) =
            e.Handled <- true
            let s = new obj()
            if (e.KeyCode = Keys.Home) then diff(0,-999)
            if (e.KeyCode = Keys.Up) then diff(0,-1)
            if (e.KeyCode = Keys.Down) then diff(0,1)
            if (e.KeyCode = Keys.End) then diff(0,999)
            if (e.KeyCode = Keys.Left) then diff(-1,0)
            if (e.KeyCode = Keys.Right) then diff(1,0)
        this.KeyDown.Add(keyDown)

        // colourVariation
        let colourVariation() =
            if (this.varnDGV.Rows.Count > 0) then
                let dedn = if this.showToolStripMenuItem.Checked then 2 else 0
                for r = 0 to this.varnDGV.Rows.Count-dedn-1 do
                    for c = 0 to this.varnDGV.Rows.[0].Cells.Count-1 do
                        this.varnDGV.Rows.[r].Cells.[c].Style.ForeColor <- Color.DarkGreen
                        this.varnDGV.Rows.[r].Cells.[c].Style.BackColor <- if c % 2 = 0 then Color.Honeydew else BOARD_SQUARE_COLOUR_WHITE
                let fndrw = Array.zeroCreate this.varnDGV.Rows.[0].Cells.Count
                for r = 0 to this.varnDGV.Rows.Count-dedn-1 do
                    if (this.varnDGV.Rows.[0].Cells.Count > 2) then
                        for c = 2 to this.varnDGV.Rows.[0].Cells.Count-1 do
                            if (not fndrw.[c] && this.varnDGV.Rows.[r].Cells.[c].Value.ToString() = this.varnDGV.Rows.[r].Cells.[c - 2].Value.ToString()) then
                                this.varnDGV.Rows.[r].Cells.[c].Style.ForeColor <- if this.allToolStripMenuItem.Checked then Color.Gray else this.varnDGV.Rows.[r].Cells.[c].Style.BackColor
                            else
                                fndrw.[c] <- true
                                if (c % 2 = 0) then fndrw.[c + 1] <- true else fndrw.[c - 1] <- true
        // updateVariation
        let updateVariation() =
            let varndat = Varn.lines(CurrVarn)
            let numcols = if varndat.Length > 0 then varndat.[0].Length else 0
            let numvars = numcols / 2
            this.varnDGV.Columns.Clear()
            this.varnDGV.ColumnCount <- numcols
            for i = 1 to numvars do
                this.varnDGV.Columns.[(i - 1) * 2].Name <- (i).ToString() + "w"
                this.varnDGV.Columns.[(i - 1) * 2 + 1].Name <- (i).ToString() + "b"
            for i = 0 to varndat.Length-1 do
                let line = varndat.[i]
                let row = new DataGridViewRow()
                for item in line do
                    row.Cells.Add(new DataGridViewTextBoxCell(Value = item))|>ignore
                row.HeaderCell.Value <- (i + 1).ToString()
                this.varnDGV.Rows.Add(row)|>ignore
            this.varnDGV.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.DisplayedCells)
            this.showToolStripMenuItem.Checked <- false
            colourVariation()
            this.showToolStripMenuItem.Checked <- true
            this.varnDGV.Refresh()

        // Main Analysis Panel
        // updAnl
        let updAnl(e) = 
            if this.showToolStripMenuItem.Checked then 
                let strs = Varn.cur2txt(CurrVarn)
                let vnpsns = CurrVarn.brchs
                let currBms = Seq.map2 (Eng.strmvl2bms (Eng.loadLineStore())) strs vnpsns|> Seq.toArray
                //clear current items
                this.Analrtb.Clear()
                //set heading font a write
                let fBold = new Font("Tahoma", 8.0f, FontStyle.Bold)
                let fNorm = new Font("Tahoma", 8.0f, FontStyle.Regular)
                this.Analrtb.SelectionFont <- fBold
                this.Analrtb.SelectionColor <- Color.DarkGreen
                this.Analrtb.SelectedText <- "Missing Best Moves by Line" + Environment.NewLine
                //load filtered results and write
                for i = 0 to currBms.Length-1 do
                    if (i + 1 >= int(this.ln0numericUpDown.Value) && i + 1 <= int(this.ln1numericUpDown.Value)) then
                        let bms = currBms.[i]
                        let mutable line = (i + 1).ToString() + "."
                        for j = 0 to bms.Length-1 do
                            //add items if staisfy filters
                            if ((this.BcheckBox.Checked && this.WcheckBox.Checked) || (this.BcheckBox.Checked && not bms.[j].bisw) || (this.WcheckBox.Checked && bms.[j].bisw)) then
                                if (bms.[j].bnum >= int(this.mv0numericUpDown.Value) && bms.[j].bnum <= int(this.mv1numericUpDown.Value)) then
                                    if this.scrcheckBox.Checked then
                                        line <- line + " " + bms.[j].bstr + "(" + bms.[j].bscr.ToString() + ")"
                                    else
                                        line <- line + " " + bms.[j].bstr
                        this.Analrtb.SelectionFont <- fNorm
                        this.Analrtb.SelectionColor <- Color.Black
                        this.Analrtb.SelectedText <- line + Environment.NewLine
                //update range values
                this.mv1numericUpDown.Value <- min this.mv1numericUpDown.Value (decimal(if this.showToolStripMenuItem.Checked then this.varnDGV.RowCount-3 else this.varnDGV.RowCount-1))
                if (currBms.Length < int(this.ln1numericUpDown.Value) && currBms.Length > 0) then this.ln1numericUpDown.Value <- decimal(currBms.Length)
        this.WcheckBox.CheckStateChanged.Add(updAnl)
        this.BcheckBox.CheckStateChanged.Add(updAnl)
        this.ln0numericUpDown.ValueChanged.Add(updAnl)
        this.ln1numericUpDown.ValueChanged.Add(updAnl)
        this.mv0numericUpDown.ValueChanged.Add(updAnl)
        this.mv1numericUpDown.ValueChanged.Add(updAnl)
        this.scrcheckBox.CheckStateChanged.Add(updAnl)
        // Extra Analysis lines
        // addAnal
        let addAnal() =
            let currAnls = Eng.getanls(Eng.loadLineStore(), CurrVarn)
            let vnpsns = CurrVarn.brchs
            let row = new DataGridViewRow()
            for item in currAnls do
                row.Cells.Add(new DataGridViewTextBoxCell(Value = item.depth.ToString()))|>ignore
                row.Cells.Add(new DataGridViewTextBoxCell(Value = item.scr.ToString()))|>ignore
            row.HeaderCell.Value <- "d:scr"
            this.varnDGV.Rows.Add(row)|>ignore
            let row = new DataGridViewRow()
            let mutable ct = 0
            for item in currAnls do
                row.Cells.Add(new DataGridViewTextBoxCell(Value = item.bmPGN))|>ignore
                row.Cells.Add(new DataGridViewTextBoxCell(Value = item.rmPGN))|>ignore
                ct <- ct + 1
            row.HeaderCell.Value <- "best"
            this.varnDGV.Rows.Add(row)|>ignore
            this.varnDGV.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.DisplayedCells)
            //colour the rows
            for r = this.varnDGV.Rows.Count - 2 to this.varnDGV.Rows.Count-1 do
                for c = 0 to this.varnDGV.Rows.[0].Cells.Count-1 do
                    if (this.varnDGV.Rows.[this.varnDGV.Rows.Count - 2].Cells.[2 * (c / 2) + 1].Value.ToString().StartsWith("-") ||
                        this.varnDGV.Rows.[this.varnDGV.Rows.Count - 2].Cells.[2 * (c / 2) + 1].Value.ToString().Length = 1) then
                        //set default
                        this.varnDGV.Rows.[r].Cells.[c].Style.ForeColor <- Color.Honeydew
                        //colour the columns
                        this.varnDGV.Rows.[r].Cells.[c].Style.BackColor <- Color.DarkGreen
                    else
                        //set highlight colour
                        this.varnDGV.Rows.[r].Cells.[c].Style.ForeColor <- Color.OrangeRed
                        //colour the columns
                        this.varnDGV.Rows.[r].Cells.[c].Style.BackColor <- Color.LightGreen
            this.varnDGV.Refresh()
            updAnl()
            this.showToolStripMenuItem.Checked <- true
        // remAnal
        let remAnal() =
            this.varnDGV.Rows.RemoveAt(this.varnDGV.Rows.Count - 1)
            this.varnDGV.Rows.RemoveAt(this.varnDGV.Rows.Count - 1)
            this.varnDGV.Refresh()
            this.showToolStripMenuItem.Checked <- false
        // reldAnl
        let reldAnl(e) =
            if this.showToolStripMenuItem.Checked then remAnal()
            addAnal()
        this.reloadAnalysisResultsToolStripMenuItem.Click.Add(reldAnl)
        this.reldtoolStripButton.Click.Add(reldAnl)
        // show
        let show(e) =
            this.showToolStripMenuItem.Checked <- not this.showToolStripMenuItem.Checked
            if (this.showToolStripMenuItem.Checked && this.varnDGV.ColumnCount > 0) then addAnal() else remAnal()
        this.showToolStripMenuItem.Click.Add(show)
        this.engresToolStripButton.Click.Add(show)
        // delLine
        let delLine(e) =
            let selvar,_ = sv_sm()
            if (MessageBox.Show("Do you want to delete line " + (selvar + 1).ToString() + " ?", "Delete Line", MessageBoxButtons.YesNo) = DialogResult.Yes) then
                CurrVarn <- Varn.del CurrVarn selvar
                updateVariation()
                if this.showToolStripMenuItem.Checked then addAnal()
        this.delLinetoolStripMenuItem.Click.Add(delLine)
        this.delLinetoolStripButton.Click.Add(delLine)
        this.deleteLineToolStripMenuItem.Click.Add(delLine)
        // actions
        // orientBoard
        let orientBoard() =
            for f = 0 to 7 do
                for r = 0 to 7 do
                    let picSquare = this.m_picSquares.[f, r]
                    if CurrVarn.isw then
                        picSquare.Left <- f * SQUARE_SIZE + 1
                        picSquare.Top <- 7 * SQUARE_SIZE - r * SQUARE_SIZE + 1
                        for indCtrl = 0 to this.BoardPanel.Controls.Count-1 do
                            let strName = this.BoardPanel.Controls.[indCtrl].Name
                            if (strName.StartsWith("lblFile")) then
                                let iFileSize = Convert.ToInt32(strName.Substring(7, strName.Length - 7)) * SQUARE_SIZE
                                this.BoardPanel.Controls.[indCtrl].Left <- iFileSize + 30
                            else if (strName.StartsWith("lblRank")) then
                                let iRankSize = Convert.ToInt32(strName.Substring(7, strName.Length - 7)) * SQUARE_SIZE
                                this.BoardPanel.Controls.[indCtrl].Top <- 7 * SQUARE_SIZE - iRankSize + 16
                    else
                        picSquare.Left <- 7 * SQUARE_SIZE - f * SQUARE_SIZE + 1
                        picSquare.Top <- r * SQUARE_SIZE + 1
                        for indCtrl = 0 to this.BoardPanel.Controls.Count-1 do
                            let strName = this.BoardPanel.Controls.[indCtrl].Name
                            if (strName.StartsWith("lblFile")) then
                                let iFileSize = Convert.ToInt32(strName.Substring(7, strName.Length - 7)) * SQUARE_SIZE
                                this.BoardPanel.Controls.[indCtrl].Left <- 7 * SQUARE_SIZE - iFileSize + 30
                            else if (strName.StartsWith("lblRank")) then
                                let iRankSize = System.Convert.ToInt32(strName.Substring(7, strName.Length - 7)) * SQUARE_SIZE
                                this.BoardPanel.Controls.[indCtrl].Top <- iRankSize + 16
        // newv
        let newv(e) =
            let formNew = ffrmNew()
            if (formNew.ShowDialog(this) = DialogResult.OK) then
                CurrVarn <- Varn.cur(formNew.nmTextBox.Text, (formNew.colComboBox.Text = "White"))
                this.varnLabel.Text <- formNew.colComboBox.Text + " - " + formNew.nmTextBox.Text
                updateVariation()
                CurrPosn <- Posn.st()
                updateLV()
                orientBoard()
                renderBoard(-1,[])
        this.newToolStripMenuItem.Click.Add(newv)
        this.newToolStripButton.Click.Add(newv)
        // openv
        let openv(e) =
            let formLoad = ffrmLoad()
            if (formLoad.ShowDialog(this) = DialogResult.OK) then
                CurrVarn <- Varn.loada(formLoad.varnComboBox.Text, (formLoad.colComboBox.Text = "White"))
                this.varnLabel.Text <- formLoad.colComboBox.Text + " - " + formLoad.varnComboBox.Text
                updateVariation()
                if (CurrVarn.brchs.Length > 0) then
                    if (this.showToolStripMenuItem.Checked) then addAnal()
                    updAnl()
                    orientBoard()
                    diff(-999,-999)
                else
                    CurrPosn <- Posn.st()
                    updateLV()
        this.openToolStripMenuItem.Click.Add(openv)
        this.openToolStripButton.Click.Add(openv)
        // save
        let save(e) =
            let msg = Varn.save(CurrVarn)
            MessageBox.Show(msg)|>ignore
        this.saveToolStripMenuItem.Click.Add(save)
        this.saveToolStripButton.Click.Add(save)
        // saveAs
        let saveAs(e) =
            let formSaveAs = ffrmSaveAs()
            if (formSaveAs.ShowDialog(this) = DialogResult.OK) then CurrVarn <- Varn.saveas(CurrVarn, formSaveAs.nmTextBox.Text)
        this.saveAsToolStripMenuItem.Click.Add(saveAs)
        // del
        let del(e) =
            let formDelete = ffrmDelete()
            if (formDelete.ShowDialog(this) = DialogResult.OK) then
                Varn.delete(formDelete.varnComboBox.Text, (formDelete.colComboBox.Text = "White"))
                MessageBox.Show("Variation " + formDelete.varnComboBox.Text + " deleted.")|>ignore
        this.delToolStripMenuItem.Click.Add(del)
        this.delToolStripButton.Click.Add(del)
        this.deleteToolStripMenuItem.Click.Add(del)
        // print
        let print(prv) =
            let printer = new DGVPrinter()
            printer.Title <- "Lizard Chess - Variation Report"
            printer.SubTitle <- (if CurrVarn.isw then "White - " else "Black - ") + CurrVarn.name
            printer.PrintRowHeaders <- Nullable true
            printer.RowHeaderCellStyle <- this.varnDGV.DefaultCellStyle.Clone()
            printer.SubTitleFormatFlags <- StringFormatFlags.LineLimit ||| StringFormatFlags.NoClip
            printer.PageNumbers <- true
            printer.PageNumberInHeader <- false
            printer.PorportionalColumns <- false
            printer.HeaderCellAlignment <- StringAlignment.Near
            printer.Footer <- "Lizard Chess"
            printer.FooterSpacing <- 15.0f
            if prv then printer.PrintPreviewDataGridView(this.varnDGV) else printer.PrintDataGridView(this.varnDGV)
        this.printPreviewToolStripMenuItem.Click.Add(fun _ -> print(true))
        this.prevToolStripButton.Click.Add(fun _ -> print(true))
        this.printToolStripMenuItem.Click.Add(fun _ -> print(false))
        this.printToolStripButton.Click.Add(fun _ -> print(false))
        //exit
        this.exitToolStripMenuItem.Click.Add(fun _ -> Application.Exit())
        // testrun
        let testrun(rnd) =
            let formTest = ffrmTest(CurrVarn.isw, CurrVarn.name, rnd)
            if (formTest.ShowDialog(this) = DialogResult.OK) then
                // load run test form using the parameters from formTest
                // Set cursor as hourglass
                Application.UseWaitCursor <- true
                Application.DoEvents()
                let formRunTest = ffrmRunTest((formTest.colComboBox.Text = "White"), formTest.varnComboBox.Text, rnd)
                Application.UseWaitCursor <- false
                formRunTest.ShowDialog(this)|>ignore
        this.testrunlinToolStripMenuItem.Click.Add(fun _ -> testrun(false))
        this.testrunlinToolStripButton.Click.Add(fun _ -> testrun(false))
        this.testrunToolStripMenuItem.Click.Add(fun _ -> testrun(true))
        this.testrunToolStripButton.Click.Add(fun _ -> testrun(true))
        // all
        let all(e) = 
            this.allToolStripMenuItem.Checked <- not this.allToolStripMenuItem.Checked
            colourVariation()
        this.allToolStripMenuItem.Click.Add(all)
        this.allToolStripButton.Click.Add(all)
        this.showAllMovesToolStripMenuItem.Click.Add(all)
        // options
        let options(e) =
            let formEngOpts = ffrmEngOpts()
            formEngOpts.ShowDialog(this)|>ignore
        this.optionsToolStripMenuItem.Click.Add(options)
        // analyse
        let analyse(e) =
            let formAnal = ffrmAnal(CurrVarn.isw, CurrVarn.name)
            formAnal.ShowDialog(this)|>ignore
        this.analyseToolStripMenuItem.Click.Add(analyse)
        this.analyseToolStripButton.Click.Add(analyse)
        // anlCmd
        let anlCmd(all,sv) =
            if (this.varnDGV.RowCount > 0) then
                //launch Eng.exe with these parameters
                let selvar,_ = sv_sm()
                let proc = new System.Diagnostics.Process()
                proc.StartInfo.FileName <- Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), "LizardChessEng.exe");
                proc.StartInfo.Arguments <- (if CurrVarn.isw then "W " else "B ") + CurrVarn.name + (if all then " ALL " else " END ") + (if sv then sv.ToString() else "")
                proc.Start() |> ignore
        this.analyseVAllToolStripMenuItem.Click.Add(fun _ -> anlCmd(true,false))
        this.analyseVToolStripMenuItem.Click.Add(fun _ -> anlCmd(false,false))
        this.analyseLAllToolStripMenuItem.Click.Add(fun _ -> anlCmd(true,true))
        this.analyseLToolStripMenuItem.Click.Add(fun _ -> anlCmd(false,true))
        // pgnCmd
        let pgnCmd(e) =
            if (this.varnDGV.RowCount > 0) then
                //get pgn for the line
                let selvar,_ = sv_sm()
                let pgn = Varn.psn2pgn(CurrVarn.brchs.[selvar])
                //put in clipboard
                Clipboard.SetText(pgn)
        this.copyToPGNToolStripMenuItem.Click.Add(pgnCmd)
        // contents
        let contents(e) = Help.ShowHelp(this.Parent, Path.Combine(Application.StartupPath, "LizardChess.chm"))
        this.contentsToolStripMenuItem.Click.Add(contents)
        this.helpToolStripButton.Click.Add(contents)
        // index
        let index(e) = Help.ShowHelpIndex(this.Parent, Path.Combine(Application.StartupPath, "LizardChess.chm"))
        this.indexToolStripMenuItem.Click.Add(index)
        // testopts
        let testopts(e) =
            let formTestOpts = ffrmTestOpts()
            formTestOpts.ShowDialog(this)|>ignore
        this.testoptsToolStripMenuItem.Click.Add(testopts)
        // results
        let results(rnd) =
            let formTestRes = ffrmTestRes(rnd)
            formTestRes.ShowDialog(this)|>ignore
        this.resultslinToolStripMenuItem.Click.Add(fun _ -> results(false))
        this.testreslinToolStripButton.Click.Add(fun _ -> results(false))
        this.resultsToolStripMenuItem.Click.Add(fun _ -> results(true))
        this.testresToolStripButton.Click.Add(fun _ -> results(true))
        // extall
        let ext(all) =
            if this.showToolStripMenuItem.Checked then
                let sv,sm = sv_sm()
                let currAnls = Eng.getanls(Eng.loadLineStore(), CurrVarn)
                CurrVarn <- if all then Eng.extall(CurrVarn, currAnls) else Eng.extlin(CurrVarn, currAnls, sv)
                updateVariation()
                addAnal()
                diff(sv,sm)
        this.extToolStripMenuItem.Click.Add(fun _ -> ext(false))
        this.extToolStripButton.Click.Add(fun _ -> ext(false))
        this.extendLineToolStripMenuItem.Click.Add(fun _ -> ext(false))
        this.extallToolStripMenuItem.Click.Add(fun _ -> ext(true))
        this.extendAllToolStripMenuItem.Click.Add(fun _ -> ext(true))
        this.extallToolStripButton.Click.Add(fun _ -> ext(true))
        // asr
        this.asrToolStripMenuItem.Click.Add(fun _ -> System.Diagnostics.Process.Start("http://www.asr-services.com/")|>ignore)
        // arc
        this.arcToolStripMenuItem.Click.Add(fun _ -> System.Diagnostics.Process.Start("http://www.arc-trust.org/")|>ignore)
        // BOARD
        // mouseDown
        let mouseDown(s:obj,e:MouseEventArgs) =
            if not (e.Button <> MouseButtons.Left) then
                let picFrom = s:?>PictureBox
                let squareFrom = Convert.ToInt32(picFrom.Tag)
                let pcidFrom = CurrPosn.Sqs.[squareFrom]
                let pieceFrom = if pcidFrom= -1 then Piece.blank else CurrPosn.Pcs.[pcidFrom] 
                if (pcidFrom <> -1 && pieceFrom.pcIsWhite = CurrPosn.IsWhite) then
                    picFrom.Image <- null
                    picFrom.Refresh()
                    this.m_curPieceCursor <- this.m_acurPieceCursors.[Posn.imageIndex(pcidFrom, CurrPosn)]
                    this.pnlEdging.Cursor <- this.m_curPieceCursor
                    // Mark possible moves
                    m_squareTo <- -1
                    let movesPossible = Posn.GenerateLegalMoves(pcidFrom, CurrPosn)
                    renderBoardColours(squareFrom,movesPossible)
                    this.pnlEdging.Refresh()
                    if (picFrom.DoDragDrop(pieceFrom, DragDropEffects.Move) = DragDropEffects.Move) then
                        let blnMoveMade = ref false
                        let pcidTo = CurrPosn.Sqs.[m_squareTo]
                        if (pcidTo = -1 || CurrPosn.Pcs.[pcidTo].pcIsWhite <> CurrPosn.IsWhite) then
                            let ml1 = movesPossible |> List.filter (fun m -> m.To = m_squareTo)
                            if ml1.Length>0 then
                                let move =
                                    if ml1.Length>1 then
                                        let formPieceSelector = ffrmPieceSelector(pieceFrom.pcIsWhite)
                                        formPieceSelector.ShowDialog(this)|>ignore
                                        ml1|>List.filter(fun m -> m.mvName = m_MoveNameSelected)|>List.head
                                    else ml1.[0]
                                CurrPosn <- Posn.DoMove(move, CurrPosn)
                                updateLV()
                                blnMoveMade := true
                                renderBoard(-1,[])
                                if (CurrVarn.isw = Mov.Isw(move)) then
                                    CurrVarn <- Varn.add CurrVarn CurrPosn
                                    updateVariation()
                                    if (this.showToolStripMenuItem.Checked) then addAnal()
                                highlightCell()

                        if (not !blnMoveMade) then
                            this.m_picSquares.[Ref.fi.[squareFrom], Ref.ri.[squareFrom]].Image <- this.imgPieces.Images.[Posn.imageIndex(pcidFrom, CurrPosn)]
                            renderBoardColours(-1,[])
                    else
                        this.m_picSquares.[Ref.fi.[squareFrom], Ref.ri.[squareFrom]].Image <- this.imgPieces.Images.[Posn.imageIndex(pcidFrom, CurrPosn)]
                        renderBoardColours(-1,[])
                    this.pnlEdging.Cursor <- Cursors.Default
        // mouseUp
        let mouseUp(e) = ()
        // dragOver
        let dragOver(e:DragEventArgs) = e.Effect <- DragDropEffects.Move
        // dragDrop
        let dragDrop(pb:PictureBox,e) = m_squareTo <- Convert.ToInt32(pb.Tag)
        // giveFeedback
        let giveFeedback(e:GiveFeedbackEventArgs) =
            if (e.UseDefaultCursors <> false) then e.UseDefaultCursors <- false
            if (this.pnlEdging.Cursor <> this.Cursor) then this.pnlEdging.Cursor <- this.m_curPieceCursor
        // createBoard
        let createBoard() =
            for r = 0 to 7 do
                let lblRank = new Label()
                lblRank.BackColor <- Color.Transparent
                lblRank.ForeColor <- BOARD_SQUARE_COLOUR_BLACK
                lblRank.Font <- new Font("Arial", 12.0F, FontStyle.Bold, GraphicsUnit.Point, byte(0))
                lblRank.Name <- "lblRank" + r.ToString()
                lblRank.Size <- new Size(SQUARE_SIZE / 2, SQUARE_SIZE)
                lblRank.Text <- Ref.rs.[r]
                lblRank.TextAlign <- ContentAlignment.MiddleCenter
                lblRank.Left <- 0
                lblRank.Top <- (8 - 1) * SQUARE_SIZE - r * SQUARE_SIZE + 16
                this.BoardPanel.Controls.Add(lblRank)
            this.m_picSquares <- Array2D.create 8 8 (new PictureBox())
            for f = 0 to 7 do
                let lblFile = new Label()
                lblFile.BackColor <- Color.Transparent
                lblFile.ForeColor <- BOARD_SQUARE_COLOUR_BLACK
                lblFile.Font <- new Font("Arial", 12.0F, FontStyle.Bold, GraphicsUnit.Point, byte(0))
                lblFile.Name <- "lblFile" + f.ToString()
                lblFile.Size <- new Size(SQUARE_SIZE, SQUARE_SIZE / 2)
                lblFile.Text <- Ref.fs.[f]
                lblFile.TextAlign <- ContentAlignment.MiddleCenter
                lblFile.Left <- f * SQUARE_SIZE + 30
                lblFile.Top <- 8 * SQUARE_SIZE + 24
                this.BoardPanel.Controls.Add(lblFile)
            for f = 0 to 7 do
                for r = 0 to 7 do
                    let picSquare = new PictureBox()
                    picSquare.Name <- "picSquare" + f.ToString() + r.ToString()
                    picSquare.Size <- new Size(SQUARE_SIZE, SQUARE_SIZE)
                    picSquare.SizeMode <- PictureBoxSizeMode.CenterImage
                    picSquare.Tag <- r * 8 + f
                    picSquare.MouseDown.Add(fun e -> mouseDown(picSquare,e))
                    picSquare.MouseUp.Add(mouseUp)
                    picSquare.DragDrop.Add(fun e -> dragDrop(picSquare,e))
                    picSquare.DragOver.Add(dragOver)
                    picSquare.GiveFeedback.Add(giveFeedback)
                    picSquare.AllowDrop <- true
                    this.pnlEdging.Controls.Add(picSquare)
                    this.m_picSquares.[f, r] <- picSquare
        // mvslv
        let mvslv(e) =
            if (this.mvsListView.SelectedItems.Count = 1) then
                let items = this.mvsListView.SelectedItems
                let mv = items.[0].Tag.ToString()
                let move = Posn.FndMv(mv,CurrPosn)
                CurrPosn <- Posn.DoMove(move, CurrPosn)
                updateLV()
                renderBoard(-1,[])
                highlightCell()
        this.mvsListView.Click.Add(mvslv)
        
        // load
        let load(e) =
            this.pnlEdging.Visible <- false
            this.LoadCursors()
            createBoard()
            orientBoard()
            renderBoard(-1,[])
            this.pnlEdging.Visible <- true
            //make varndgv double buffered
            let dgvType = this.varnDGV.GetType()
            let pi = dgvType.GetProperty("DoubleBuffered", BindingFlags.Instance ||| BindingFlags.NonPublic)
            pi.SetValue(this.varnDGV, true, null)
        this.Load.Add(load)
        this
    [<STAThread>]
    do 
        Application.Run(frm)